package cn.xawl.interview.thread;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author lh
 * @date 2020/08/25 20:25:04
 *
 * 自旋锁
 * 实现一个自旋锁
 * 自旋锁的好处：循环比较获取锁直到成功为止
 *
 * 通过CAS操作完成自旋锁：A线程先进来调用myLock方法，持有锁5秒钟。
 * B后进来发现A线程持有锁，不是null，所以只能自旋等待，直到A释放锁后B随后抢到锁
 *
 * A	尝试获取🔒
 * A	获取到🔒
 * A	开始执行
 * B	尝试获取🔒
 * A	释放🔒
 * B	获取到🔒
 * B	开始执行
 * B	释放🔒
 *
 **/
public class SpinLockDemo {

    AtomicReference<Thread> atomicReference = new AtomicReference<>(null);

    public void lock() {
        System.out.println(Thread.currentThread().getName() + "\t尝试获取🔒");
        //CAS操作：如果发现当前主内存中的线程为null，那么就将自己放入内存
        while (!atomicReference.compareAndSet(null, Thread.currentThread())) {
            //如果CAS返回false，就进入循环，等待其他线程释放锁
            //让循环执行慢一些，减少CPU消耗
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + "\t获取到🔒");
    }

    public void unlock() {
        //释放锁操作：CAS操作，比较内存中与当前线程是否是同一个线程，如果是，就更新为null，让其他所进入lock
        atomicReference.compareAndSet(Thread.currentThread(), null);
        System.out.println(Thread.currentThread().getName() + "\t释放🔒");
    }

    public static void main(String[] args) {
        SpinLockDemo spinLockDemo = new SpinLockDemo();
        new Thread(() -> {
            //获取锁
            spinLockDemo.lock();
            //睡眠五秒，模拟当前线程占用五秒锁
            System.out.println(Thread.currentThread().getName() + "\t开始执行");
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //释放锁
            spinLockDemo.unlock();
        }, "A").start();

        //让A线程率先执行
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            spinLockDemo.lock();
            System.out.println(Thread.currentThread().getName() + "\t开始执行");
            spinLockDemo.unlock();
        }, "B").start();

    }

}
